package com.zj.util;

import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * Created by zj on 2018/3/27.
 */

public class CameraUtil {
    private static final String TAG = "CameraUtil";

    private static CameraUtil cameraUtil = null;

    private Camera camera = null;
    private Camera.Size previewSize;
    private int cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;

    public CameraUtil() {
        if (Objects.isNull(camera)) {
            camera = Camera.open(cameraId);
        }
    }

    public static synchronized CameraUtil getInstance() {
        if (Objects.isNull(cameraUtil)) {
            cameraUtil = new CameraUtil();
        }

        return cameraUtil;
    }

    public void init() {
        Camera.Parameters parameters = camera.getParameters();

        parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

        camera.setParameters(parameters);
        camera.setDisplayOrientation(90);
    }

    public void initPreview(SurfaceHolder holder, int width, int height, Camera.PreviewCallback callback) {
        Camera.Parameters parameters = camera.getParameters();

        previewSize = getOptimalPreviewSize(parameters.getSupportedPreviewSizes(), width, height);
        parameters.setPreviewSize(previewSize.width, previewSize.height);
        parameters.setPictureSize(previewSize.width, previewSize.height);
        parameters.setPreviewFormat(ImageFormat.NV21);
        Log.e(TAG, "getPreviewSize: preview width = " + previewSize.width + " height = " + previewSize.height);

        camera.setParameters(parameters);
        try {
            camera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        camera.setPreviewCallback(callback);
        camera.startPreview();
    }

    public void initFaceDetect(Camera.FaceDetectionListener listener) {
        Camera.Parameters params = camera.getParameters();
        Objects.requireNonNull(params.getMaxNumDetectedFaces());

        camera.setFaceDetectionListener(listener);
        camera.startFaceDetection();
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = 720;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    public void setCameraId(int cameraId) {
        if (Objects.nonNull(camera)) {
            camera.release();
        }

        camera = Camera.open(cameraId);
        this.cameraId = cameraId;
    }

    public int getCameraId() {
        return cameraId;
    }

    public void autoFocus(Camera.AutoFocusCallback autoFocusCallback) {
        camera.autoFocus(autoFocusCallback);
    }

    public Size getPreviewSize() {
        return new Size(previewSize.width, previewSize.height);
    }

    private void releaseCamera() {
        if (Objects.nonNull(camera)) {
            // 停止预览
            camera.setPreviewCallback(null);
            camera.stopPreview();
            // 停止人脸检测
            camera.setFaceDetectionListener(null);
            camera.stopFaceDetection();

            camera.release();
            camera = null;
        }
    }

    public void release() {
        releaseCamera();
        cameraUtil = null;
    }

    public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
                                     int viewWidth, int viewHeight) {
        // Need mirror for front camera.
        matrix.setScale(mirror ? -1 : 1, 1);
        // This is the value for android.hardware.Camera.setDisplayOrientation.
        matrix.postRotate(displayOrientation);
        // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
        // UI coordinates range from (0, 0) to (width, height).
        matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
        matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
    }

    public Rect getFaceRect(Camera.Face face, Size size) {
        boolean isMirror = false;
        int Id = cameraUtil.getCameraId();
        Matrix mMatrix = new Matrix();
        RectF rectF = new RectF();

        if (Id == Camera.CameraInfo.CAMERA_FACING_BACK) {
            isMirror = false; //后置Camera无需mirror
        } else if (Id == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            isMirror = true;  //前置Camera需要mirror
        }
        prepareMatrix(mMatrix, isMirror, 90, size.getWidth(), size.getHeight());
        Log.e(TAG, "getFaceRect: size.width = " + size.getWidth() + " size.height = " + size.getHeight());
        mMatrix.postRotate(0); //Matrix.postRotate默认是顺时针
        rectF.set(face.rect);
        mMatrix.mapRect(rectF);

        return new Rect(Math.round(rectF.left), Math.round(rectF.top),
                Math.round(rectF.right), Math.round(rectF.bottom));
    }

}
